package com.hrg.rds.infra.executor;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.hrg.rds.biz.config.RdsConfiguration;
import com.hrg.rds.biz.robot.domain.AgvVo;
import com.hrg.rds.biz.waybill.domain.instance.*;
import com.hrg.rds.biz.waybill.domain.template.cmd.CmdParam;
import com.hrg.rds.biz.waybill.domain.template.cmd.CmdType;
import com.hrg.rds.biz.waybill.mapper.CmdInstanceMapper;
import com.hrg.rds.biz.waybill.service.CmdInstanceService;
import com.hrg.rds.biz.waybill.service.WaybillInstanceService;
import com.hrg.rds.infra.async.callback.ICallback;
import com.hrg.rds.infra.async.callback.IWorker;
import com.hrg.rds.infra.async.worker.WorkResult;
import com.hrg.rds.infra.async.wrapper.WorkerWrapper;
import com.hrg.sys.common.constant.UserConstants;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Slf4j
public class CmdInstanceWorker implements IWorker<List<CmdParam>, List<CmdParam>>, ICallback<List<CmdParam>, List<CmdParam>> {
	// 运单实例中除去流程类命令外的所有命令的worker封装
	@Getter
	private String id;
	private final CmdType cmdType;
	private CmdInstanceResult cmdInstanceResult = CmdInstanceResult.init();

	// 自动拼接两个命令到执行链路中，用于更新运单实例开始和成功
	private final CmdInstanceContext cmdInstanceContext;

	private static final Map<String, AgvVo> AgvIpMap = RdsConfiguration.AgvIpMap;

	public CmdInstanceWorker(String id, CmdType cmdType, CmdInstanceContext cmdInstanceContext) {
		this.id = id;
		this.cmdType = cmdType;
		this.cmdInstanceContext = cmdInstanceContext;
	}

	@Override
	public void begin() {
		log.debug("CmdInstanceWorker begin");
		try {
			if (checkIfNotSysCmd() && cmdInstanceContext.checkIfNotExit()) {
				String cmdInstanceId = cmdInstanceContext.getCmdInstanceId();
				CmdInstanceMapper mapper = SpringUtil.getBean(CmdInstanceMapper.class);
				CmdInstance cmdInstance = new CmdInstance();
				cmdInstance.setId(cmdInstanceId);
				Date now = new Date();
				cmdInstance.setStartTime(now);
				cmdInstance.setUpdateBy(UserConstants.SYS_USER);
				cmdInstance.setUpdateTime(now);
				// 更新 start_time
				mapper.updateById(cmdInstance);
			}
		} catch (Exception e) {
			//cmdInstanceContext.setException();
			log.error("CmdInstanceWorker begin error", e);
		}
	}

	@Override
	public void result(boolean success, List<CmdParam> inputParams, WorkResult<List<CmdParam>> workResult) {
		log.debug("CmdInstanceWorker result");

		if (cmdInstanceContext.checkIfExit() && checkIfLastCmd()) {
			WaybillInstanceService waybillInstanceService = SpringUtil.getBean(WaybillInstanceService.class);
			WaybillInstance waybillInstance = new WaybillInstance();
			String waybillInstanceId = cmdInstanceContext.getWaybillInstanceId();
			waybillInstance.setId(waybillInstanceId);
			if (cmdInstanceContext.getTerminated()) {
				waybillInstance.setWaybillInstanceStatus(WaybillInstanceStatus.Terminated);
			} else {
				waybillInstance.setWaybillInstanceStatus(WaybillInstanceStatus.Exception);
			}
			Date now = new Date();
			waybillInstance.setEndTime(now);
			waybillInstance.setUpdateBy(UserConstants.SYS_USER);
			waybillInstance.setUpdateTime(now);
			waybillInstanceService.updateById(waybillInstance);
			// 清除运单实例
			cmdInstanceContext.removeWaybillInstanceVo();
		}
		if (cmdInstanceContext.checkIfExit()) {
			return;
		}
		if (checkIfNotSysCmd()) {
			// 配置的命令执行成功
			CmdInstance cmdInstance = null;
			CmdInstanceService cmdInstanceService = null;
			try {
				cmdInstanceService = SpringUtil.getBean(CmdInstanceService.class);
				String cmdInstanceId = cmdInstanceContext.getCmdInstanceId();
				cmdInstance = new CmdInstance();
				cmdInstance.setId(cmdInstanceId);
				Date now = new Date();
				cmdInstance.setEndTime(now);
				cmdInstance.setUpdateBy(UserConstants.SYS_USER);
				cmdInstance.setUpdateTime(now);
				cmdInstance.setCode(CmdInstanceCode.Complete);
				cmdInstance.setInputParams(JSONUtil.toJsonStr(inputParams));
				cmdInstance.setOutputParams(JSONUtil.toJsonStr(cmdInstanceResult.getOutputParams()));
				cmdInstanceService.updateById(cmdInstance);
				// 每次执行完一个命令，更新agvVo中的运单信息，方便前端查看
				WaybillInstanceVo waybillInstanceVo = cmdInstanceContext.getWaybillInstanceVo();
				AgvIpMap.values().stream()
						.filter(agvVo -> agvVo.getId().equals(waybillInstanceVo.getAgvId()))
						.findFirst()
						.ifPresent(agvVo -> agvVo.onWaybillInstanceRefreshed(waybillInstanceVo));
			} catch (Exception e) {
				/*if (cmdInstanceService != null && cmdInstance != null) {
					Date now = new Date();
					cmdInstance.setUpdateBy(UserConstants.SYS_USER);
					cmdInstance.setUpdateTime(now);
					cmdInstance.setInputParams(JSONUtil.toJsonStr(inputParams));
					cmdInstance.setCode(CmdInstanceCode.Exception);
					cmdInstance.setMsg(e.getMessage());
					cmdInstanceService.updateById(cmdInstance);
				}*/
				//cmdInstanceContext.setException();
				log.error("CmdInstanceWorker result error", e);
			}
		} else if (cmdType.equals(CmdType.EndCmd)) {
			// 运单执行结束，更新agvVo中的运单信息，方便前端查看
			String waybillInstanceId = cmdInstanceContext.getWaybillInstanceId();
			AgvIpMap.values().stream()
					.filter(agvVo -> agvVo.getWaybillInstanceId().equals(waybillInstanceId))
					.findFirst().ifPresent(AgvVo::onWaybillInstanceFinished);
		}
	}

	@Override
	public List<CmdParam> action(List<CmdParam> cmdParams, Map<String, WorkerWrapper> allWrappers) {
		log.debug("CmdInstanceWorker action");
		List<CmdParam> outputParams = null;
		while (checkIfContinue()) {
			try {
				CmdExecutor cmdExecutor = SpringUtil.getBean(cmdType.name(), CmdExecutor.class);
				cmdInstanceResult = cmdExecutor.execute(cmdParams, cmdInstanceContext);
				if (cmdInstanceResult == null) {
					//cmdInstanceContext.setException();
					log.error("CmdInstanceWorker action error, cmdInstanceResult is null");
				}
				//重试
				if (cmdInstanceResult.getStatus().equals(CmdExecutorStatus.Retry)) {
					// 重试间隔
					String msg = cmdInstanceResult.getRetryMsg();
					if (StrUtil.isNotBlank(msg)) {
						// 把错误信息存储下来
						// 前端处理反馈到执行线程中
						CmdInstanceService cmdInstanceService = SpringUtil.getBean(CmdInstanceService.class);
						String cmdInstanceId = cmdInstanceContext.getCmdInstanceId();
						CmdInstance cmdInstance = new CmdInstance();
						cmdInstance.setId(cmdInstanceId);
						Date now = new Date();
						cmdInstance.setUpdateBy(UserConstants.SYS_USER);
						cmdInstance.setUpdateTime(now);
						cmdInstance.setMsg(msg);
						cmdInstanceService.updateById(cmdInstance);
					}
					if (cmdInstanceResult.getSleep4Retry() != null) {
						TimeUnit.MILLISECONDS.sleep(cmdInstanceResult.getSleep4Retry().toMillis());
					} else {
						TimeUnit.MILLISECONDS.sleep(cmdExecutor.getRetryInterval().toMillis());
					}
				}
				// 异常
				if (cmdInstanceResult.getStatus().equals(CmdExecutorStatus.Exception)) {
					String msg = cmdInstanceResult.getExceptionMsg();
					if (StrUtil.isNotBlank(msg)) {
						// 把错误信息存储下来
						// 更新实例异常信息
						CmdInstanceService cmdInstanceService = SpringUtil.getBean(CmdInstanceService.class);
						String cmdInstanceId = cmdInstanceContext.getCmdInstanceId();
						CmdInstance cmdInstance = new CmdInstance();
						cmdInstance.setId(cmdInstanceId);
						Date now = new Date();
						cmdInstance.setUpdateBy(UserConstants.SYS_USER);
						cmdInstance.setUpdateTime(now);
						cmdInstance.setMsg(msg);
						cmdInstanceService.updateById(cmdInstance);
					}
					//cmdInstanceContext.setException();
					// 更新运单实例状态为异常
					WaybillInstanceService waybillInstanceService = SpringUtil.getBean(WaybillInstanceService.class);
					WaybillInstance waybillInstance = new WaybillInstance();
					String waybillInstanceId = cmdInstanceContext.getWaybillInstanceId();
					waybillInstance.setId(waybillInstanceId);
					waybillInstance.setWaybillInstanceStatus(WaybillInstanceStatus.Exception);
					Date now = new Date();
					waybillInstance.setUpdateBy(UserConstants.SYS_USER);
					waybillInstance.setUpdateTime(now);
					waybillInstanceService.updateById(waybillInstance);
				}
				outputParams = cmdInstanceResult.getOutputParams();
			} catch (Exception e) {
				//cmdInstanceContext.setException();
				log.error("CmdInstanceWorker action error", e);
			}
		}
		return outputParams;
	}

	private boolean checkIfNotSysCmd() {
		return !cmdType.equals(CmdType.StartCmd)
				&& !cmdType.equals(CmdType.EndCmd)
				&& !cmdType.equals(CmdType.JoinCmd);
	}

	private boolean checkIfLastCmd() {
		return cmdType.equals(CmdType.EndCmd)
				|| cmdType.equals(CmdType.JoinCmd);
	}

	private boolean checkIfContinue() {
		return !(cmdInstanceResult.getStatus().equals(CmdExecutorStatus.Success))
				&& cmdInstanceContext.checkIfNotExit();
	}
}
